import {sendToPortByWritable} from "@/utils/toSerial.js";
import {toByteArray, calculateChecksum} from "@/utils/byteParser.js";
import {useUserProfile} from "@/stores/useUserProfile.js";
import {crc16Zero} from "@/utils/refactor/crc16.js";


const userProfile = useUserProfile()
const {commandConfig} = userProfile

/**
 * 生成指令参数
 * @param instructionName 指令名称(适配枚举参数)
 * @param paramsToUpdate 指令参数
 * @returns {*|*[]}
 */
export const generateInstruction = (instructionName, paramsToUpdate) => {
    // 找到对应的指令
    const instruction = commandConfig.optical.instruction_set.find(inst => inst.label === instructionName);

    // 如果找到了指令，则更新其参数
    if (instruction) {
        instruction.params = instruction.params.map(p => {
            // 如果参数名称匹配，就更新它的默认值
            if (paramsToUpdate[p.name] !== undefined) {
                console.log(paramsToUpdate, p.name)
                // 如果是枚举类型且结果不包含0x 就直接映射value
                if (p.enum && !paramsToUpdate[p.name].includes("0x")) {
                    // 如果是传入枚举值就通过key映射value(先反转键值对)
                    const reversedEnum = Object.fromEntries(
                        Object.entries(p.enum).map(([key, value]) => [value, key])
                    );
                    p.default = reversedEnum[paramsToUpdate[p.name]];
                    console.log("---->", reversedEnum[paramsToUpdate[p.name]])
                } else {
                    // 直接传入源码的情况
                    p.default = paramsToUpdate[p.name];
                }

            }
            return p;
        });
        // 计算校验和
        let slicedParams = instruction.params.slice(4, -2);

        // 转换为十六进制并计算累加和
        const hexSum = calculateChecksum(toByteArray(slicedParams));
        // 刷新校验和的值
        instruction.params.find(item => item.name === "校验和").default = `0x${hexSum}`
        return instruction.params; // 返回更新后的params
    } else {
        // 如果没有找到指令，返回空数组或其他适当的错误处理
        return [];
    }
};

/**
 * 生成指令(高级的)
 * @param name 指令名称
 * @param params 指令参数
 * @returns {Uint8Array}
 */
export const generateCommand = (name, params) => {
    return toByteArray(generateInstruction(name, params))
}
/**
 * 切换重构, 启动对应模块重构
 * @param writable
 * @param module 启动重构模块源码值 -> 0x4
 * @returns {Promise<void>}
 */
export const ready = async (writable, module) => {
    // 系统模式切换至重构模式并启动重构
    const switchMode = toByteArray(generateInstruction("系统工作模式切换", {"系统模式选择": "0x04"}))
    const startRefactor = toByteArray(generateInstruction("光学头重构启动", {"重构类型": module, "启动/停止": "0x55"}))
    console.log(switchMode, "系统模式切换")
    console.log(startRefactor, "光学头重构启动")
    await sendToPortByWritable(writable, switchMode)
    // 延迟等待
    await new Promise(resolve => setTimeout(resolve, 1000))
    await sendToPortByWritable(writable, startRefactor)
    await new Promise(resolve => setTimeout(resolve, 1000))
}

/**
 * 数据写入
 * @param writable
 * @param writeType 需要写入模块的对应源码值
 * @returns {Promise<void>}
 */
export const dataWrite = async (writable, writeType) => {
    // 光学头重构数据写入
    const dwrite = toByteArray(generateInstruction("光学头重构数据写入", {"写入类型": writeType}))
    console.log(dwrite, "光学头重构数据写入")
    await sendToPortByWritable(writable, dwrite)
}

/**
 * 对应模块软件加载
 * @param writable
 * @param loadType 需要加载模块的对应源码值
 * @returns {Promise<void>}
 */
export const softwareUpdate = async (writable, loadType) => {
    // 软件加载
    const loadCmd = toByteArray(generateInstruction("光学头软件加载", {"加载类型": loadType}))
    console.log(loadCmd, "光学头软件加载")
    await sendToPortByWritable(writable, loadCmd)
}

/**
 * 停止对应模块的重构
 * @param writable
 * @param module
 * @returns {Promise<void>}
 */
export const stopRefactorByModule = async (writable, module) => {
    // 停止某一模块的重构 == 光学头重构启动指令(停止)
    const switchMode = toByteArray(generateInstruction("系统工作模式切换", {"系统模式选择": "0x04"}))
    const stopRefactor = toByteArray(generateInstruction("光学头重构启动", {"重构类型": module, "启动/停止": "0xaa"}))
    console.log(switchMode, "系统模式切换")
    console.log(stopRefactor, "光学头重构启动")
    await sendToPortByWritable(writable, switchMode)
    await new Promise(resolve => setTimeout(resolve, 1000))
    await sendToPortByWritable(writable, stopRefactor)
    await new Promise(resolve => setTimeout(resolve, 1000))
}

export const cancelProtection = async (writable) => {
    // 停止某一模块的重构 == 光学头重构启动指令(停止)
    const cp = toByteArray(generateInstruction("相机星敏自闭环保护设置", {
        '目的地址': '0x44',
        '数据长度': 10,
        '指令码': 141,
        '相机温度上限': 45,
        '相机温度下限': 246,
        '星敏温度上限': 45,
        '星敏温度下限': 246,
        '相机自闭环温度保护使能': '0xaa',
        '星敏自闭环温度保护使能': '0xaa',
        '相机自闭环异常次数保护使能': '0xaa',
        '星敏自闭环异常次数保护使能': '0xaa',
        '参数是否保存': '0xaa'
    }))
    console.log("相机星敏自闭环保护设置", cp)
    await sendToPortByWritable(writable, cp)

}

/**
 * 写入帧数据到光学头重构线
 * @param reader 读取器, 来自于重构文件
 * @param writer  写入器, 来自于重构线的串口
 * @param byteArray  写入的字节数据
 * @param chunkSize  每次写入块大小
 * @param delay  每次写入延时
 * @param callback 每次写入完成后的回调函数
 * @returns {Promise<void>}
 */
export const writeChunks = async (reader, writer, byteArray, chunkSize = 264, delay = 20, callback = null) => {
    let count = 0;
    let {done, value} = await reader.read();

    while (!done) {
        let offset = 0;
        while (offset < value.length) {
            try {
                const chunk = value.slice(offset, offset + chunkSize);
                await writer.ready; // 处理背压
                await writer.write(chunk);
                await new Promise(resolve => setTimeout(resolve, delay)); // 按需调整
                offset += chunkSize;
                count += 1;

                if (callback) {
                    callback(parseFloat((count / (byteArray.size / chunkSize) * 100).toFixed(2)));
                }
            } catch (e) {
                console.error(e);
                writer.releaseLock();
                await writer.close();
                return;
            }
        }
        ({done, value} = await reader.read());
    }

    await writer.close(); // 确保数据刷新
};

/**
 * 把一个普通数组转为的Uint8Array
 * @returns {Uint8Array}
 * @param inputArray 由无符号整型组成的数组[0xeb90, "0x22"]
 */
export function toUint8Array(inputArray) {
    const result = [];

    inputArray.forEach((num) => {
        if (num <= 0xFF) {
            result.push(num);
        } else {
            const bytes = [];
            while (num > 0) {
                bytes.unshift(num & 0xFF)
                num >>= 8;
            }
            result.push(...bytes);
        }
    });

    return new Uint8Array(result);
}

/**
 * 按字节数拆分一个整数, splitIntoBytes(2, 1) -> [0x00, 0x01]
 * @param byteCount 分割数
 * @param number 被切分的十进制整数
 * @returns {*[]}
 */
export function splitIntoBytes(byteCount, number) {
    let result = [];

    for (let i = 0; i < byteCount; i++) {
        let byte = (number >> (8 * (byteCount - 1 - i))) & 0xFF;
        result.push(`0x${byte.toString(16).padStart(2, '0')}`);
    }

    return result;
}


/**
 * 根据指定spi重构文件按照协议生成数据帧
 * @param file
 * @param targetAddr
 */
export function generateFrame(file, targetAddr) {
    return new Promise((resolve, reject) => {
        if (!file) {
            alert('Please select a SPI file.');
            reject('No file selected');
            return;
        }

        const reader = new FileReader();
        reader.onload = function (e) {
            const content = new Uint8Array(e.target.result);
            const fileLength = content.length;
            let index = 0;
            const packTotal = Math.ceil(fileLength / 252) + 1;

            const byteArrayChunks = [];  // 用来存储每个数据块

            // 文件内容转hexString
            const hexString = Array.from(new Uint8Array(content))
                .map(byte => byte.toString(16).padStart(2, '0'))
                .join(' ');

            // 计算crc16校验值
            const crcValue = crc16Zero(hexString);
            console.log("首帧crc校验值:", crcValue.toString(16))
            // 创建首帧
            let firstFramePart = [
                0xeb90, 0x22, targetAddr, 0xff, 0xbd, "0x00", "0x00", 0x33,
                packTotal, ...splitIntoBytes(4, fileLength), ...splitIntoBytes(2, crcValue)
            ];

            const uint8Array = toUint8Array(firstFramePart);
            const checksum = uint8Array.slice(5, uint8Array.length).reduce((x, y) => x + y, 0) & 0xff;
            const fullFstFrame = toUint8Array([...uint8Array, ...new Array(260 - 17 + 1).fill(0), checksum, 0x09d7]);
            console.log(fullFstFrame)
            byteArrayChunks.push(fullFstFrame);

            let start = 0;
            while (start < fileLength) {
                let next_ = start + 252;
                if (next_ <= fileLength) {
                    index += 1;
                    // 截取数据每次252
                    let dataSegment = content.slice(start, next_);
                    let segment = [
                        ...[0xeb90, 0x22, targetAddr, 0xff, 0xbe, ...splitIntoBytes(2, index), 0xfc],
                        ...Array.from(dataSegment),
                    ];
                    let checksum = toUint8Array(segment).slice(5, 261).reduce((sum, val) => sum + val, 0) & 0xff;
                    const segmentBuffer = toUint8Array([...segment, checksum, 0x09d7]);
                    byteArrayChunks.push(segmentBuffer);
                    start += 252;
                } else {
                    index += 1
                    // 最后一帧实际长度
                    const lastFrameLength = fileLength - start
                    console.log(lastFrameLength);
                    let zeroBytes = new Array(252).fill(0);
                    let segment = [
                        ...[0xeb90, 0x22, targetAddr, 0xff, 0xbe, ...splitIntoBytes(2, index), lastFrameLength],
                        ...Array.from(zeroBytes),
                    ];
                    let checksum = toUint8Array(segment).slice(5, 261).reduce((sum, val) => sum + val, 0) & 0xff;
                    const segmentBuffer = toUint8Array([...segment, checksum, 0x09d7]);
                    console.log("lastFrame:", segmentBuffer)
                    byteArrayChunks.push(segmentBuffer);
                    break;
                }
            }

            // 逐步创建 Blob
            const blob = new Blob(byteArrayChunks, {type: 'application/octet-stream'});

            resolve(blob);  // 返回 Blob 对象
        };

        reader.onerror = function () {
            reject('Error reading file');
        };

        reader.readAsArrayBuffer(file);
    });
}


export const moduleStatusMap = {
    "主控模块": {
        "ready": "0x1",
        "wait": {"主控重构状态": "0x1"},
        "finished": "0x6", // 写入完成
    },
    "电机模块": {
        "ready": "0x5", // 准备接收帧
        "wait": {"电机重构状态": "0x5", "主控重构状态": "0x1"}, // 接收
        "finished": "0x0a" // 升级完成
    },
    "星敏模块": {
        "ready": "0x5", // 准备接收帧
        "wait": {"星敏重构状态": "0x5", "主控重构状态": "0x1"}, // 接收
        "finished": "0xf" // 升级完成
    },
    "相机模块": {
        "ready": "0x5", // 准备接收帧
        "wait": {"相机重构状态": "0x5", "主控重构状态": "0x1"}, // 接收
        "finished": "0xf" // 升级完成
    },
}

/*一些模块状态定义, 如果遥测变动此处也需要随之修改*/
export const asModuleStatusMap = {
    "0x11": {
        "ready": "0x1",
        "wait": {"主控重构状态": "0x01"},
        "written": "0x6", // 写入完成
        "finished": "0x0"
    },
    "0x44": {
        "ready": "0x05", // 准备接收帧
        "wait": {"电机重构状态": "0x05", "主控重构状态": "0x01"}, // 接收
        "written": "0x0A", // 写入完成
        "finished": "0x0"

    },
    "0x33": {
        "ready": "0x5", // 准备接收帧
        "wait": {"星敏重构状态": "0x5", "主控重构状态": "0x01"}, // 接收
        "written": "0xf", // 写入完成
        "finished": "0x00"
    },
    "0x22": {
        "ready": "0x5", // 准备接收帧
        "wait": {"相机重构状态": "0x5", "主控重构状态": "0x01"}, // 接收
        "written": "0xf", // 写入完成
        "finished": "0x0"
    },
}

/*重构的配置包含各模块不同分区的指令参数及各种标识*/
export const refactorDefaultConfig = [
    {
        "region": "golden",
        "module": "0x33",
        "write": "0x55",
        "load": "0x55",
        "as": "xm",
        "label": "星敏golden"
    },
    {
        "region": "user",
        "module": "0x33",
        "write": "0x55",
        "load": "0x66",
        "as": "xm",
        "label": "星敏user"
    },
    // ---------------------------
    {
        "region": "golden",
        "module": "0x22",
        "write": "0x44",
        "load": "0x33",
        "as": "camera",
        "label": "相机golden"
    },
    {
        "region": "user",
        "module": "0x22",
        "write": "0x44",
        "load": "0x44",
        "as": "camera",
        "label": "相机user"
    },
    // -------------------------------
    {
        "region": "golden",
        "module": "0x44",
        "write": "0x77",
        "load": "0x77",
        "as": "motor",
        "label": "电机golden"
    },
    {
        "region": "user",
        "module": "0x44",
        "write": "0x66",
        "load": "0x88",
        "as": "motor",
        "label": "电机user"
    },
    {
        "region": "golden",
        "module": "0x11",
        "write": "0x22",
        "load": "0x11",
        "as": "mc",
        "label": "主控golden"
    },
    {
        "region": "user",
        "module": "0x11",
        "write": "0x11",
        "load": "0x22",
        "as": "mc",
        "label": "主控user"
    }
]